import { traverseNode } from "./traverseNode.js";

/**
 * @description 创建字符串字面量节点
 * @param {*} value 字符串
 * @returns
 */
function createStringLiteral(value) {
  return {
    type: "StringLiteral",
    value,
  };
}

/**
 * @description 创建 标识符 节点
 * @param {*} name 标识符名称
 */
function createIdentifier(name) {
  return {
    type: "Identifier",
    name,
  };
}

/**
 * @description 创建数组表达式节点
 * @param {*} elements 元素节点
 */
function createArrayExpression(elements) {
  return {
    type: "ArrayExpression",
    elements,
  };
}

/**
 * @description 创建调用表达式节点
 * @param {*} callee
 * @param {*} arguments
 */
function createCallExpression(callee, args) {
  return {
    type: "CallExpression",
    callee: createIdentifier(callee),
    args,
  };
}

/**
 * @description 转换根节点
 * @param {*} node 节点
 */
function transformRoot(node) {
  return () => {
    /* 如果节点不是根节点则不做任何操作 */
    if (node.type !== "Root") return;
    /* node 是根节点的话，那么根节点的第一个子节点就是模板的根节点（不考虑有多个根节点的情况） */
    const vnodeJSAST = node.children[0].jsNode;
    /* 创建 render 函数的声明语句节点，将vnodeJSAST 作为 render 函数体的返回语句 */
    node.jsNode = {
      type: "FunctionDecl",
      id: {
        type: "Idnetifier",
        name: "render",
      },
      params: [],
      body: [
        {
          type: "ReturnStatement",
          return: vnodeJSAST,
        },
      ],
    };
    console.log("🚀 ~ return ~ node.jsNode ====> ", node.jsNode);
  };
}

/**
 * @description 转换元素节点
 * @param {*} node 元素节点
 * @param {*} context 转换的上下文对象
 * @returns
 */
function transformElement(node, context) {
  return () => {
    /* 如果节点的类型不是元素节点则不做任何操作 */
    if (node.type !== "Element") return;

    /**
     * 1.创建 h 函数调用语句
     * h 函数调用的第一个参数是标签名称，因此我们以node.tag来创建一个字符串字面量节点
     * 作为第一个参数
     */
    const callExpression = createCallExpression("h", [createStringLiteral(node.tag)]);

    /**
     * 2.处理 h 函数调用的参数
     */

    node.children.length === 1
      ? callExpression.args.push(node.children[0].jsNode)
      : callExpression.args.push(
          createArrayExpression([node.children.map((child) => child.jsNode)])
        );

    node.jsNode = callExpression;
    console.log("🚀 ~ return ~ node.jsNode ====> ", node.jsNode);
  };
}

/**
 * @description 转换文本节点
 * @param {*} node 文本节点
 * @param {*} context 转换的上下文对象
 * @returns
 */
function transformText(node, context) {
  return () => {
    /* 如果节点类型不是文本则不做任何操作 */
    if (node.type !== "Text") return;
    /* 文本节点对应的 Javascript AST 节点其实就是一个字符串字面量 */
    /* 所以只需要使用 node.content 创建一个 StringLiteral 类型的节点即可 */
    /* 最后将文本节点对应的JavaScript AST 节点添加到 node.jsNode 属性下 */
    node.jsNode = createStringLiteral(node.content);
    console.log("🚀 ~ return ~ node.jsNode ====> ", node.jsNode);
  };
}

/**
 * @description 转换模板AST ==> JavaScript AST
 * @param {*} ast
 */
export function transformJavaScriptAST(ast) {
  /* 转换的上下文对象 */
  const context = {
    /* 当前转换的节点 */
    currentNode: null,
    /* 当前转换的节点的父节点 */
    parent: null,
    /* 当前转换的节点在父节点的children中的位置索引 */
    currentIndex: 0,
    /* 替换节点 */
    replaceNode(node) {
      /* 在父组件中找到当前节点的位置，将其替换为新的节点 */
      context.parent.children[context.currentIndex] = node;
      /* 另外，需要将当前节点也替换为新的节点 */
      context.currentNode = node;
    },
    /* 删除节点 */
    removeNode() {
      /* 根据currentIndex在父组的children中找到当前节点，并将其从children中删除 */
      context.parent.children.splice(context.currentIndex, 1);
      /* 将当前节点置空 */
      context.currentNode = null;
    },
    /* 不同type对应的转换函数列表 */
    transforms: [transformRoot, transformElement, transformText],
  };

  /* 递归遍历所有节点 */
  traverseNode(ast, context);
}
